package com.dszq.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dszq.apis.article.IArticleClient;
import com.dszq.common.constants.WemediaConstants;
import com.dszq.common.constants.WmNewsMessageConstants;
import com.dszq.common.exception.CustomException;
import com.dszq.model.common.dtos.PageResponseResult;
import com.dszq.model.common.dtos.ResponseResult;
import com.dszq.model.common.enums.AppHttpCodeEnum;
import com.dszq.model.wemedia.dtos.NewsAuthDto;
import com.dszq.model.wemedia.dtos.WmNewsDto;
import com.dszq.model.wemedia.dtos.WmNewsPageReqDto;

import java.util.*;
import java.util.stream.Collectors;

import com.dszq.model.wemedia.pojos.WmMaterial;
import com.dszq.model.wemedia.pojos.WmNews;
import com.dszq.model.wemedia.pojos.WmNewsMaterial;
import com.dszq.model.wemedia.pojos.WmUser;
import com.dszq.model.wemedia.vo.WmNewsVo;
import com.dszq.utils.thread.WmThreadLocalUtil;
import com.dszq.wemedia.mapper.WmMaterialMapper;
import com.dszq.wemedia.mapper.WmNewsMapper;
import com.dszq.wemedia.mapper.WmNewsMaterialMapper;
import com.dszq.wemedia.mapper.WmUserMapper;
import com.dszq.wemedia.service.WmNewsAutoScanService;
import com.dszq.wemedia.service.WmNewsService;
import com.dszq.wemedia.service.WmNewsTaskService;
//import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * [一句话描述该类的功能]
 *
 * @author : [Lenovo]
 * @version : [v1.0]
 * @createTime : [2024/4/18 15:24]
 */
@Service
@Transactional
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {
    @Autowired
    private WmNewsMapper wmNewsMapper;
    /**
     * 查询所有新闻信息
     *
     * @param wmNewsPageReqDto 包含新闻查询条件和分页信息的数据传输对象
     * @return 返回新闻信息的响应结果，包含新闻列表和分页信息
     */
    @Override
    public ResponseResult findAll(WmNewsPageReqDto wmNewsPageReqDto) {
        // 检查传入的查询条件对象是否为null
        if (wmNewsPageReqDto==null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 校验查询参数的合法性
        wmNewsPageReqDto.checkParam();
        // 获取当前登录用户
        WmUser wmUser = WmThreadLocalUtil.getUser();
        // 检查用户是否登录
        if (wmUser == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 初始化分页对象
        IPage page = new  Page(wmNewsPageReqDto.getPage(),wmNewsPageReqDto.getSize());
        // 构建查询条件
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 根据状态进行查询
        lambdaQueryWrapper.eq(Objects.nonNull(wmNewsPageReqDto.getStatus()), WmNews::getStatus, wmNewsPageReqDto.getStatus());
        // 根据频道ID进行查询
        lambdaQueryWrapper.eq(Objects.nonNull(wmNewsPageReqDto.getChannelId()),WmNews::getChannelId,wmNewsPageReqDto.getChannelId());
        // 根据发布日期范围进行查询
        if(wmNewsPageReqDto.getBeginPubDate()!=null && wmNewsPageReqDto.getEndPubDate()!=null){
            lambdaQueryWrapper.between(WmNews::getPublishTime,wmNewsPageReqDto.getBeginPubDate(),wmNewsPageReqDto.getEndPubDate());
        }
        // 根据关键字进行查询
        if (StringUtils.isNotBlank(wmNewsPageReqDto.getKeyword()))
        {
            lambdaQueryWrapper.like(WmNews::getTitle,wmNewsPageReqDto.getKeyword());
        }
        // 查询当前登录用户的文章
        lambdaQueryWrapper.eq(WmNews::getUserId,wmUser.getId());

        // 发布时间倒序查询
        lambdaQueryWrapper.orderByDesc(WmNews::getCreatedTime);
        // 执行分页查询
        page = page(page,lambdaQueryWrapper);
        // 构建响应结果
        ResponseResult responseResult = new PageResponseResult(wmNewsPageReqDto.getPage(),wmNewsPageReqDto.getSize(), (int) page.getTotal());
        responseResult.setData(page.getRecords());
        return responseResult;
    }
    @Autowired
    private WmNewsTaskService wmNewsTaskService;
    /**
     * 提交新闻信息
     * 发布修改文章或保存为草稿
     * @param wmNewsDto 新闻数据传输对象，包含新闻的全部或部分信息
     * @return ResponseResult 返回操作结果，成功或失败，以及可能的错误信息
     */
    @Override
    public ResponseResult submitNews(WmNewsDto wmNewsDto) {
        // 条件判断，检查输入参数是否有效
        if(wmNewsDto == null || wmNewsDto.getContent() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 创建新闻实体并复制DTO中的属性
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto, wmNews);

        // 处理封面图片信息
        if (wmNewsDto.getImages() != null && wmNewsDto.getImages().size() > 0) {
            String images = StringUtils.join(wmNewsDto.getImages(), ",");
            wmNews.setImages(images);
        }

        // 如果类型为自动，将类型设置为null
        if (wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            wmNews.setType(null);
        }

        // 保存或更新新闻信息
        saveOrUpdateWmNews(wmNews);

        // 判断是否发布为草稿，若是则结束方法
        if (wmNewsDto.getStatus().equals(WmNews.Status.NORMAL.getCode())) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }

        // 保存文章内容图片与素材的关系
        List<String> materialImages = extractUrlInfo(wmNews.getContent());
        saveRelativeInfoForContent(materialImages, wmNews.getId());

        // 保存文章封面图片与素材的关系，并处理自动类型时的封面图片匹配
        saveRelativeInfoForCover(wmNewsDto, wmNews, materialImages);
        //审核文章
        wmNewsTaskService.addNewsToTask(wmNews.getId(),wmNews.getPublishTime());
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /**
     * 保存封面相关信息。
     * 本函数负责根据提供的新闻素材信息，自动设定新闻的封面类型，并保存封面图片与新闻的关系。
     *
     * @param wmNewsDto 新闻数据传输对象，包含新闻的初始信息。
     * @param wmNews 新闻实体，用于存储处理后的新闻信息。
     * @param materialImages 新闻素材中的图片列表。
     */
    private void saveRelativeInfoForCover(WmNewsDto wmNewsDto, WmNews wmNews, List<String> materialImages) {
        List<String> images = wmNewsDto.getImages();
        // 自动设置封面类型逻辑
        if(wmNewsDto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)){
            // 根据图片数量自动设定封面类型，并处理图片列表
            if(materialImages.size() >= 3){
                // 多图情况
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
                images = materialImages.stream().limit(3).collect(Collectors.toList());
            }else if(materialImages.size() >= 1 && materialImages.size() < 3){
                // 单图情况
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
                images = materialImages.stream().limit(1).collect(Collectors.toList());
            }else {
                // 无图情况
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }

            // 保存图片列表至新闻实体，若存在图片
            if(images != null && images.size() > 0){
                wmNews.setImages(StringUtils.join(images,","));
            }
            // 更新新闻实体
            updateById(wmNews);
        }
        // 保存封面图片与新闻的关系，若存在图片
        if(images != null && images.size() > 0){
            saveRelativeInfo(images,wmNews.getId(),WemediaConstants.WM_COVER_REFERENCE);
        }
    }


    /**
     * 处理文章内容图片与素材的关系
     * @param materialImages
     * @param newsId
     */
    private void saveRelativeInfoForContent(List<String> materialImages, Integer newsId) {
        saveRelativeInfo(materialImages,newsId,WemediaConstants.WM_CONTENT_REFERENCE);
    }
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    /**
     * 保存文章图片与素材的关系到数据库中
     * @param materialImages 图片素材的URL列表
     * @param newsId 文章的ID
     * @param type 素材的类型
     */
    private void saveRelativeInfo(List<String> materialImages, Integer newsId, Short type) {
        // 检查素材图片列表是否非空且非零长度
        if(materialImages!=null &&!materialImages.isEmpty())
        {
            // 从数据库中查询与素材图片URL匹配的素材信息
            List<WmMaterial> dbMaterialList = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl,materialImages));
            // 检查查询结果是否为空
            if (dbMaterialList==null||dbMaterialList.size()==0)
            {
                // 如果查询结果为空，抛出素材引用失败异常
                throw  new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
            }
            // 检查查询到的素材数量是否与提供的素材URL列表长度一致
            if (dbMaterialList.size()!=materialImages.size())
            {
                // 如果不一致，抛出素材引用失败异常
                throw  new CustomException(AppHttpCodeEnum.MATERIAL_REFERENCE_FAIL);
            }
            // 提取素材ID列表，为建立关系做准备
            List<Integer> idList = dbMaterialList.stream().map(WmMaterial::getId).collect(Collectors.toList());
            // 保存素材与新闻的关系到数据库
            wmNewsMaterialMapper.saveRelations(idList,newsId,type);
        }
    }

    /**
     * 提取文章内容中的图片信息
     * @param content 包含图片信息的文章内容，格式为JSON数组字符串
     * @return 返回一个包含所有图片URL的列表
     */
    private List<String> extractUrlInfo(String content) {
        List<String> materialImages = new ArrayList<>(); // 用于存储提取到的图片URL

        // 将JSON字符串解析为Map列表
        List<Map> maps = JSON.parseArray(content, Map.class);
        for (Map map : maps) {
            // 判断当前元素是否为图片类型
            if(map.get("type").equals("image")){
                String imgUrl = (String) map.get("value"); // 获取图片URL
                materialImages.add(imgUrl); // 将图片URL添加到列表中
            }
        }
        return materialImages; // 返回提取到的图片URL列表
    }


    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    private void saveOrUpdateWmNews(WmNews wmNews) {
        //补全属性
        wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable((short) 1);

        if (wmNews.getId()!=null)
        {
            //修改
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getId,wmNews.getId()));
            updateById(wmNews);
        }else {
            //新增
            save(wmNews);
        }
    }

    /**
     * 根据新闻ID获取一条新闻的详细信息。
     *
     * @param id 新闻的唯一标识符，从URL路径变量中获取。
     * @return 返回一个包含新闻详细信息的ResponseResult对象。
     */
    @Override
    public ResponseResult getOneNew(Integer id) {
        // 检查ID是否为空
        if (id == null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID); // 参数无效，返回错误结果
        }
        WmNews wmNews = wmNewsMapper.selectById(id);
        // 检查记录是否存在
        if (wmNews==null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST); // 数据不存在，返回错误结果
        }
        return ResponseResult.okResult(wmNews);
    }

    @Autowired
    private IArticleClient articleClient;
    /**
     * 根据新闻ID删除新闻
     *
     * @param id 新闻的唯一标识符
     * @return 返回操作结果，成功返回true，失败返回false
     */
    @Override
   // @GlobalTransactional
    public ResponseResult deleteNewById(Integer id) {
        // 检查ID是否为空
        if (id == null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章ID不可缺少"); // 参数无效，返回错误结果
        }
        WmNews wmNews = wmNewsMapper.selectById(id);
        // 检查记录是否存在
        if (wmNews==null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在"); // 数据不存在，返回错误结果
        }
        if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())&&wmNews.getEnable().equals(0))
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章已发布，不能删除");
        }
        wmNewsMapper.deleteById(id);
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,id));
        articleClient.deleteArticle(wmNews.getArticleId());
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
    @Autowired
    private KafkaTemplate kafkaTemplate;
    /**
     * 处理新闻的上下架操作。
     *
     * @param wmNewsDto 包含新闻相关信息的传输对象，用于指定需要进行上下架操作的新闻。
     * @return 返回一个响应结果，包含操作是否成功及其相关信息。
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto wmNewsDto) {
        // 检查ID是否为空
        if (wmNewsDto.getId() == null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章ID不可缺少"); // 参数无效，返回错误结果
        }
        WmNews wmNews = wmNewsMapper.selectOne(Wrappers.<WmNews>lambdaQuery().eq(WmNews::getId,wmNewsDto.getId()));
        // 检查记录是否存在
        if (wmNews==null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在"); // 数据不存在，返回错误结果
        }
        if (!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode()))
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"当前文章不是发布状态,不能上下架");
        }
        if(wmNewsDto.getEnable()!=null&&wmNewsDto.getEnable()>-1&&wmNewsDto.getEnable()<2)
        {
            update(Wrappers.<WmNews>lambdaUpdate().eq(WmNews::getId,wmNews.getId()).set(WmNews::getEnable,wmNewsDto.getEnable()));
            //发送消息，通知article端修改文章配置
            if(wmNews.getArticleId() != null){
                Map<String,Object> map = new HashMap<>();
                map.put("articleId",wmNews.getArticleId());
                map.put("enable",wmNewsDto.getEnable());
                kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(map));
            }
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 通过POST请求查找文章信息。
     * 这个方法接收客户端传来的新闻授权信息，根据这些信息查询相关文章，并返回查询结果。
     *
     * @param newsAuthDto 接收客户端传来的新闻授权信息数据传输对象，包含用于查询文章的条件，如页码和每页大小等。
     * @return 返回查询结果的响应体，封装了查询到的文章信息，包括文章列表和总数等。
     */
    @Override
    public ResponseResult findArticles(NewsAuthDto newsAuthDto) {
        // 校验传入的新闻授权信息参数
        newsAuthDto.checkParam();
        // 计算起始页码，用于分页查询
        Integer newsAuthDtoPage = newsAuthDto.getPage();
        newsAuthDto.setPage((newsAuthDto.getPage()-1)*newsAuthDto.getSize());
        // 根据条件查询文章列表并分页
        List<WmNewsVo> wmNewsVoList = wmNewsMapper.findListAndPage(newsAuthDto);
        // 统计符合条件的文章总数
        int count  = wmNewsMapper.findListCount(newsAuthDto);
        // 构建并返回查询结果的响应体
        ResponseResult responseResult = new PageResponseResult(newsAuthDtoPage,newsAuthDto.getSize(),count);
        responseResult.setData(wmNewsVoList);
        return responseResult;
    }
    @Autowired
    private WmUserMapper wmUserMapper;
    /**
     * 根据新闻ID查询最新的新闻信息。
     *
     * @param id 新闻的唯一标识符。
     * @return 返回一个包含查询结果的ResponseResult对象。
     */
    @Override
    public ResponseResult findOneNew(Integer id) {
        if (id==null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        WmNews wmNews = getById(id);
        if (wmNews==null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        WmNewsVo wmNewsVo = new WmNewsVo();
        BeanUtils.copyProperties(wmNews,wmNewsVo);
        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        wmNewsVo.setAuthorName(wmUser.getName());
        ResponseResult responseResult = new ResponseResult<>().ok(wmNewsVo);
        return responseResult;
    }
    @Autowired
    private WmNewsAutoScanService wmNewsAutoScanService;
    /**
     * 处理新闻认证的逻辑。
     *
     * @param newsAuthDto 包含新闻认证信息的数据传输对象，用于传递新闻的认证状态和其它必要信息。
     * @param status
     * @return 返回操作结果，包括操作是否成功、错误码等信息。
     */
    @Override
    public ResponseResult updateNews(NewsAuthDto newsAuthDto, Short status) {
        //1.检查参数
        if (newsAuthDto==null||newsAuthDto.getId()==null)
        {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.查询文章信息
        WmNews wmNews = wmNewsMapper.selectById(newsAuthDto.getId());
        if (wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.修改文章的状态
        wmNews.setStatus(status);
        if (StringUtils.isNotBlank(newsAuthDto.getMsg()))
        {
            wmNews.setReason(newsAuthDto.getMsg());
        }
        updateById(wmNews);

        //审核成功，则需要创建app端文章数据，并修改自媒体文章
        if (status.equals(WemediaConstants.WM_NEWS_AUTH_PASS))
        {
            //创建app端文章数据
            ResponseResult responseResult = wmNewsAutoScanService.saveAppArticle(wmNews);
            if (responseResult.getCode().equals(200))
            {
                wmNews.setArticleId((Long) responseResult.getData());
                wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
                updateById(wmNews);
            }
        }
        //4.返回
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
}
